<template>
  <div class="HEditTable">
    <slot name="title">
      <div class="actions flex mt-2 mb-4">
        <a-button type="primary" @click="handleAdd">新增</a-button>
        <a-button danger class="ml-3" @click="handleDelMutil">删除</a-button>

        <div v-if="isAllEdit">
          <a-button class="ml-3" @click="handleCancelAllEdit">返回</a-button>
        </div>
        <div class="flex-1 flex" v-else>
          <a-button class="ml-3" @click="handleAllEdit">批量编辑</a-button>
          <slot name="head"> </slot>
        </div>
      </div>
    </slot>
    <div class="w-full pb-1 overflow-x-auto" :class="tableClass">
      <div class="table-header">
        <table class="my-table table-layout">
          <thead class="table-thead">
            <tr
              ><th class="w-10">
                <Checkbox
                  :disabled="!rawData.length"
                  v-model:checked="checkAll"
                  @change="onCheckAllChange"
                  :indeterminate="indeterminate"
                />
              </th>
              <th class="w-10">序号</th>
              <template v-for="c in column" :key="c.label">
                <th :class="c.class || 'w-50'">{{ c.label }}</th>
              </template>
              <th v-if="!isAllEdit">操作</th>
            </tr>
          </thead>
        </table>
      </div>

      <div class="table-body">
        <table class="my-table table-layout">
          <Draggable
            group="table-draggable"
            class="tbody"
            tag="tbody"
            :component-data="{
              tag: 'tbody',
              type: 'transition-group',
              name: 'list',
            }"
            ghostClass="moving"
            :animation="180"
            :list="rawData"
            handle=".th-drag"
            item-key="key"
            @update="handleSaveAllEdit"
          >
            <template #item="{ element: item, index }">
              <tr class="tr-item">
                <td class="w-10"><Checkbox v-model:checked="item.check" /></td>
                <td class="w-10 th-drag"> <Icon icon="carbon:drag-vertical" /> {{ index + 1 }}</td>
                <template v-for="tbItem in column" :key="tbItem.label">
                  <td :class="tbItem.class || 'w-50'">
                    <template v-if="isAllEdit && tbItem.component">
                      <div class="cell-item" v-if="tbItem.parentField">
                        <component
                          :is="tbItem.component"
                          v-model:value="item[tbItem.parentField][tbItem.dataIndex]"
                          v-bind="
                            isFunction(tbItem.componentProps)
                              ? tbItem.componentProps({
                                  record: item,
                                  column: tbItem,
                                  index,
                                })
                              : tbItem.componentProps
                          "
                        />
                      </div>
                      <div class="cell-item" v-else>
                        <component
                          :is="tbItem.component"
                          v-model:value="item[tbItem.dataIndex]"
                          v-model:checked="item[tbItem.dataIndex]"
                          v-bind="
                            isFunction(tbItem.componentProps)
                              ? tbItem.componentProps({
                                  record: item,
                                  column: tbItem,
                                  index,
                                })
                              : tbItem.componentProps
                          "
                        />
                      </div>
                    </template>
                    <template v-else-if="tbItem.slot">
                      <slot
                        :name="tbItem.slot"
                        v-bind="{
                          column: tbItem,
                          record: item,
                          index,
                        }"
                      >
                      </slot>
                    </template>
                    <template v-else>
                      <div v-if="tbItem.format && isFunction(tbItem.format)">
                        {{ tbItem.format(item[tbItem.dataIndex], item, index) }}
                      </div>
                      <div v-else>{{ item[tbItem.dataIndex] }} </div>
                    </template>
                  </td>
                </template>
                <td v-if="!isAllEdit">
                  <slot
                    name="actions"
                    v-bind="{
                      record: item,
                      index,
                    }"
                  >
                    <a-button type="link" v-if="getEdit" @click.stop="handleEdit(item, index)">
                      编辑
                    </a-button>
                    <a-button type="link" danger @click.stop="handleDel(index)">删除</a-button>
                  </slot>
                </td>
              </tr>
            </template>
          </Draggable>
        </table>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
  import { PropType, defineComponent, computed, ref, watch } from 'vue';
  import {
    DatePicker,
    Radio,
    Switch,
    Checkbox,
    Select,
    Input,
    Slider,
    InputNumber,
  } from 'ant-design-vue';
  import AutoComplete from 'ant-design-vue/es/auto-complete';
  import { Icon } from '/@/components/Icon';
  import { IconPicker } from '/@/components/Icon';

  import { ApiRadioGroup, ApiSelect, ApiTreeSelect } from '/@/components/Form';

  import { useMessage } from '/@/hooks/web/useMessage';
  import { isFunction, isBoolean } from '/@/utils/is';

  import Draggable from 'vuedraggable';
  import { getDictTypeListApi } from '/@/api/admin/dict';
  import { DICT } from '/@/enums/dict';
  import { propTypes } from '/@/utils/propTypes';
  import { EditColumn } from './typing';

  export default defineComponent({
    name: 'MTable',
    components: {
      Icon,
      Select,
      Input,
      Slider,
      Switch,
      Checkbox,
      ApiSelect,
      Draggable,
      IconPicker,
      DatePicker,
      InputNumber,
      AutoComplete,
      ApiRadioGroup,
      ApiTreeSelect,
      RadioGroup: Radio.Group,
      TimePicker: DatePicker.TimePicker,
      InputTextarea: Input.TextArea,
    },
    props: {
      data: Array as PropType<Indexable[]>,
      columns: {
        require: true,
        type: Array as PropType<EditColumn[]>,
      },
      isEdit: propTypes.any,
      showAllEdit: propTypes.bool,
      tableClass: propTypes.string,
    },
    emits: ['update:data', 'add', 'edit'],
    setup(props, { emit }) {
      const { createMessage } = useMessage();
      const rawData = computed({
        get() {
          if (!props.data) return [];
          // 数据兼容处理
          if (props.columns && props.columns.length) {
            const config = props.columns[0];
            if (config.parentField) {
              props.data.forEach((el) => {
                if (!el[config.parentField!]) {
                  el[config.parentField!] = {};
                }
              });
            }
          }
          return props.data;
        },
        set(v) {
          return emit('update:data', v);
        },
      });

      const getEdit = computed(() => {
        if (isBoolean(props.isEdit)) return props.isEdit;
        return true;
      });
      const displayTypeList = ref<any[]>([]);
      if (!props.columns) {
        getDictTypeListApi({
          type: DICT.TABLE_BTN_LOCATION,
        }).then((res) => {
          if (displayTypeList.value.length <= 0) {
            res.forEach((el) => {
              el.value = el.intValue;
            });
            displayTypeList.value = res;
          }
        });
      }

      const column: EditColumn[] = [
        {
          label: '名称',
          dataIndex: 'label',
          component: 'Input',
          componentProps: {
            placeholder: '请输入名称',
          },
        },
        {
          label: '权限标识',
          dataIndex: 'code',
          component: 'Input',
          componentProps: {
            placeholder: '请输入名称',
          },
        },
        {
          label: '按钮位置',
          dataIndex: 'location',
          component: 'RadioGroup',
          componentProps: () => {
            return {
              options: displayTypeList.value,
            };
          },
          format(v) {
            const info = displayTypeList.value.find((el) => el.intValue === v);

            if (info) return info.label;
            return v;
          },
        },
        { label: '备注', dataIndex: 'remark' },
      ];

      const handleAdd = (record) => {
        emit('add', record);
      };
      const handleEdit = (record, index) => {
        record.index = index;
        emit('edit', record);
      };
      const handleDel = (index) => {
        rawData.value.splice(index, 1);
      };
      const handleDelMutil = () => {
        const arr = rawData.value || [];
        const data = arr.filter((el) => el.check);
        if (!data.length) {
          createMessage.warn('请先选择要删除的列', 0.8);
          return;
        }
        if (data.length === arr.length) {
          checkAll.value = false;
        }

        rawData.value = arr.filter((el) => !el.check);
      };

      const checkAll = ref(false);
      watch(
        () => props.data,
        (v) => {
          if (!v) return;

          if (!v.length) {
            checkAll.value = false;
          } else {
            const arr = v.filter((el) => !el.check);
            checkAll.value = !arr.length;
          }
        },
        { deep: true },
      );

      const indeterminate = computed(() => {
        const arr = rawData.value || [];
        const len = arr.filter((el) => el.check).length;
        const allLen = arr.length;

        if (len === allLen) return false;
        return !!arr.find((el) => el.check);
      });

      const onCheckAllChange = (e) => {
        for (const item of rawData.value) {
          item.check = e.target.checked;
        }
      };

      const isAllEdit = ref(props.showAllEdit);
      const handleAllEdit = () => {
        if (!rawData.value.length) return createMessage.warning('暂无数据');
        isAllEdit.value = true;
      };
      const handleSaveAllEdit = () => {};
      const handleCancelAllEdit = () => {
        isAllEdit.value = false;
      };

      return {
        column: props.columns || column,
        indeterminate,
        checkAll,
        isAllEdit,
        rawData,
        getEdit,
        handleAdd,
        handleEdit,
        handleDel,
        handleDelMutil,
        isFunction,
        onCheckAllChange,
        handleAllEdit,
        handleCancelAllEdit,
        handleSaveAllEdit,
      };
    },
  });
</script>
<style lang="less" scoped>
  .HEditTable {
    .table-layout {
      table-layout: fixed;
    }

    .table-body {
      width: 100%;
      box-sizing: border-box;

      // max-height: calc(100vh - 180px);
    }

    .my-table {
      width: 100%;
      border-radius: 4px 4px 0 0;
      box-sizing: border-box;
      text-align: left;
      color: #666;
      border-collapse: separate;
      border-spacing: 0;

      .table-thead {
        white-space: nowrap;
        text-overflow: ellipsis;
        word-break: keep-all;

        > tr > th {
          position: relative;
          font-weight: 500;
          background: @table-header-bg;
          border-bottom: 1px solid #f0f0f0;
          transition: background 0.3s ease;
        }
      }

      th,
      td {
        padding: 12px 8px;
        overflow-wrap: break-word;
      }

      .tr-item {
        margin-bottom: 5px;
        table-layout: fixed;
        white-space: nowrap;
        text-overflow: ellipsis;
        word-break: keep-all;

        &:hover {
          position: relative;
          box-shadow: @box-shadow-A1-base;

          td {
            background: @table-row-hover-bg;
          }
        }

        td {
          background-color: @table-bg;
          border-bottom: 1px solid @table-td-border;
        }

        .th-drag {
          &:hover {
            cursor: grab;
            box-shadow: 0px 4px 4px -3px #dde6f1, inset 0px 1px 4px -3px #35629a;
          }
        }
      }

      .t-action {
        position: sticky;
        right: 0;
        min-width: 100px;

        &::after {
          position: absolute;
          top: 0;
          bottom: -1px;
          left: 0;
          width: 30px;
          transform: translateX(-100%);
          transition: box-shadow 0.3s;
          content: '';
          pointer-events: none;
          box-shadow: inset -10px 0 8px -8px rgba(0, 0, 0, 0.15);
        }
      }
    }
  }
</style>
